/* 
 * File:   ipd_img.cpp
 * Author: dima
 * 
 * Created on 5 января 2015 г., 11:13
 */
#include <cstring>
#include <string>
#include "ipd_img.h"
#include "ipd_exception.h"
namespace ipd
{

ipd_img::ipd_img(unsigned char maximum_color, unsigned char minimum_color) : max_color(maximum_color), min_color(minimum_color)
{
    buf_image = NULL;
    set_width(0);
    set_height(0);
    set_color_type(undefined);
}

void ipd_img::init(int w, int h, color_model color_type, unsigned char *buf)//object initialization
{
    buf_image = NULL;
    set_width(w);
    set_height(h);
    set_color_type(color_type);
    create_image();
    copy_buf_image(buf);

}

void ipd_img::init(int w, int h, color_model color_type)//object initialization
{
    buf_image = NULL;
    set_width(w);
    set_height(h);
    set_color_type(color_type);
    create_image();
}

void ipd_img::copy_img(const ipd_img& orig)// copy image
{
    delete_image();
    set_height(orig.height);
    set_width(orig.width);
    set_color_type(orig.type_img);
    create_image();
    copy_buf_image(orig.buf_image);
}

ipd_img::~ipd_img()
{
    delete_image();
}

void ipd_img::create_image() // create buffer for image and color depending on the type of image(RGB or monochrome)
{
    delete_image();
    int len = get_size();
    buf_image = new unsigned char[len];
}

unsigned char* ipd_img::get_color(int w, int h) // get color from pixel
{
    if (h >= height || w >= width || w < 0 || h < 0 || buf_image == NULL)
        throw ipd_exception(std::string("Image parameters are incorrect"), std::string("ipd_img"));
    switch (type_img)
    {
    case tp_RGB:
    {
        unsigned char *color;
        color = buf_image + (h * width + w) * sz_RGB; // R
        return color;
        break;
    }
    case tp_mono:
    {
        unsigned char *color;
        color = buf_image + h * width + w;
        return color;
        break;
    }
    default:
    {
        throw ipd_exception(std::string("Image model is not supported (Supports RGB and monochrome image)"), std::string("ipd_img"));
    }
    }
}

void ipd_img::set_color(int w, int h, unsigned char* col) // set color from pixel
{
    if (h >= height || w >= width || w < 0 || h < 0 || col == NULL || buf_image == NULL)
        throw ipd_exception(std::string("Image parameters are incorrect"), std::string("ipd_img"));
    switch (type_img)
    {
    case tp_RGB:
    {
        check_color_value(col[0]);
        check_color_value(col[1]);
        check_color_value(col[2]);
        buf_image[(h * width + w) * sz_RGB] = col[0]; // R
        buf_image[(h * width + w) * sz_RGB + 1] = col[1]; // G
        buf_image[(h * width + w) * sz_RGB + 2] = col[2]; // B
        break;
    }
    case tp_mono:
    {
        check_color_value(*col);
        buf_image[h * width + w] = *col;
        break;
    }
    default:
    {
        throw ipd_exception(std::string("Image model is not supported (Supports RGB and monochrome image)"), std::string("ipd_img"));
    }

    }
}

unsigned int ipd_img::get_size() const // Get the number of bytes in the picture
{
    switch (type_img)
    {
    case tp_RGB:
    {
        return width * height * sz_RGB;
        break;
    }
    case tp_mono:
    {
        return width*height;
        break;
    }
    default:
    {
        throw ipd_exception(std::string("Image model is not supported (Supports RGB and monochrome image)"), std::string("ipd_img"));
    }
    }
}

ipd_img* ipd_img::operator-(const ipd_img &img)// subtraction image
{
    if (this->get_width() != img.get_width() || this->get_height() != img.get_height() || this->get_color_type() != img.get_color_type())
        throw ipd_exception(std::string("The dimension of the image does not match"), std::string("ipd_img"));
    int len = get_size();
    ipd_img *sub_img = new ipd_img();
    try
    {
        sub_img->init(this->get_width(), this->get_height(), this->get_color_type());
    }
    catch (ipd::ipd_exception &except)
    {
        delete sub_img;
        throw ipd_exception(except.what(), except.in_file());
    }
    int subtraction;
    for (int i = 0; i < len; i++)
    {
        subtraction = (int) this->buf_image[i]-(int) img.buf_image[i];
        sub_img->buf_image[i] = (subtraction + max_color) / 2;
    }
    return sub_img;
}

ipd_img* ipd_img::operator+(const ipd_img &img)// addition image
{
    if (this->get_width() != img.get_width() || this->get_height() != img.get_height() || this->get_color_type() != img.get_color_type())
        throw ipd_exception(std::string("The dimension of the image does not match"), std::string("ipd_img"));
    int len = get_size();
    ipd_img *sub_img = new ipd_img();
    try
    {
        sub_img->init(this->get_width(), this->get_height(), this->get_color_type());
    }
    catch (ipd::ipd_exception &except)
    {
        delete sub_img;
        throw ipd_exception(except.what(), except.in_file());
    }
    int addition;
    for (int i = 0; i < len; i++)
    {
        addition = (int) this->buf_image[i]+(int) img.buf_image[i];
        sub_img->buf_image[i] = addition / 2;
    }
    return sub_img;
}

ipd_img* ipd_img::operator-=(const ipd_img &img)// subtraction image(masking)
{
    if (this->get_width() != img.get_width() || this->get_height() != img.get_height() || this->get_color_type() != img.get_color_type())
        throw ipd_exception(std::string("The dimension of the image does not match"), std::string("ipd_img"));
    int len = get_size();
    int subtraction;
    for (int i = 0; i < len; i++)
    {
        subtraction = (int) this->buf_image[i]-(int) img.buf_image[i];
        this->buf_image[i] = (subtraction + max_color) / 2;
    }
    return this;
}

ipd_img* ipd_img::operator+=(const ipd_img &img)// addition image(averaging image)
{
    if (this->get_width() != img.get_width() || this->get_height() != img.get_height() || this->get_color_type() != img.get_color_type())
        throw ipd_exception(std::string("The dimension of the image does not match"), std::string("ipd_img"));
    int len = get_size();
    int addition;
    for (int i = 0; i < len; i++)
    {
        addition = (int) this->buf_image[i]+(int) img.buf_image[i];
        this->buf_image[i] = addition / 2;
    }
    return this;
}

ipd_img::ipd_img(const ipd_img& orig) : max_color(orig.max_color), min_color(orig.min_color)
{

}

void ipd_img::copy_buf_image(unsigned char *buf) // copy image buffer
{
    if (buf_image == NULL)
        throw ipd_exception(std::string("Image parameters are incorrect"), std::string("ipd_img"));
    int len = get_size();
    std::memcpy(buf_image, buf, len);
}

void ipd_img::delete_image() // delete image buffer
{
    if (buf_image != NULL)
        delete []buf_image;
    buf_image = NULL;
}

void ipd_img::check_color_value(unsigned char color)
{
    if (color > max_color || color < min_color)
    {
        throw ipd_exception(std::string("The color value is set incorrectly"), std::string("ipd_img"));
    }
}

}
